/****************************************************************************
 ***            C O N F I D E N T I A L --- G A L A C H I P                  ***    
 ****************************************************************************
 *                    file name: cms_shm.c                                    *
 *                    author: cms                                                *                
 *                    start data:  April 14th, 2020                             *
 *                    last update: Jule 18th, 2020                            *
 *--------------------------------------------------------------------------*
 *Functions:
     
 *
 ---------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/sem.h>
#include <errno.h>
#include <signal.h>

#include "cms_common.h"
#include "cms_log.h"
#include "libmdm.h"
#include "cms_mdm.h"
#include "Lzma.h"
//#include "libgc.h"

#define SHMMAX 0x2000000         /* max shared seg size (bytes) */
#ifdef CUSTOM_PRECONFIG_SUPPORT
#define PRE_CONFIG_NAME "config.pre.xml"
#endif

typedef void (*sighandler_t)(int);

/*
Description:
 CreatShmKeyFile -- If not exist shmKey, creat new one.
 
 INPUT: keyFile  --  the full name of key file. 
          
 
 OUTPUT: CMS_NOK -- fail
         CMS_OK  -- success
 
 History:
     06/18/2020 cms:created
 */
static int CreatShmKeyFile(char *keyFile){

    FILE *fp = NULL;

    fp = fopen(keyFile,"w+");
    if(fp == (FILE *)NULL){
        CMS_LOG(LL_ERROR, "Error opening Keyfile %s\n", keyFile);
        return CMS_NOK;
    }
    fclose(fp);
    
    chmod(keyFile, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
    
    return CMS_OK;
}


/*
Description:
 cmsXml_setShareMemoryReadOnly -- reset share memory to read only, for indirect access process.
                                                                          
 INPUT: xmlShmCb  -- xml share memory block.                         
          fileSize  -- the xml file need to parse to share memmory 
          cmsFileName -- cms file name.    
     
 OUTPUT: -1 -- fail                                             
           0  -- success                                                                                            
                                                                      
 History:                                                         
     06/18/2020 cms:created                                         
*/                                                                  
static int cmsXml_setShareMemoryReadOnly(cmsXmlCb_t *xmlShmCb,uint32_t fileSize, char *cmsFileName){
    key_t keys;
    int shmId, size, errsv = 0;
    char *ptr = NULL;
    char shmKeyFile[256] = {0};
    int ret=0, i=0;

    CMS_LOG(LL_FLOOD, "enter\n");
    size = fileSize;
    if(size > 0){
        /*if (size > SHMMAX){
            size = SHMMAX;
        }*/
        sprintf(shmKeyFile,"%s%s_%d", CMS_KEY_DIR, PRE_CONF_FILE_SHM, i);
        
        /*allocate share memory*/
        keys=ftok(shmKeyFile, 'a');
        errsv = errno;
        if (keys == -1){
            if (errsv == ENOENT){
                ret =CreatShmKeyFile(shmKeyFile);
                keys=ftok(shmKeyFile, 'a');
            }
            else{
                CMS_LOG(LL_ERROR, "SHM ftok errno:%x\n", errsv);
            }
        }
        shmId = shmget (keys, size, 0666 | S_IRUSR | S_IWUSR); 

        if (shmId == -1){    /*fail, something wrong!!*/
            CMS_LOG(LL_ERROR, "should not happen, shmget fail, error\n");
            return CMS_NOK;
        }else{    /*success*/
            //if (i == 0){
                ptr=(void*)shmat (shmId, 0, SHM_RDONLY);
                xmlShmCb->readOnly = 1;
            #if 0
            }
            else{
                ptr=(void*)shmat (shmId, ptr1, SHM_RND | SHM_RDONLY);
                /*do we need to check the ptr is right? (exactly at ptr+i*SHMMAX)*/
            }
            #endif
        }
        if (ptr < 0){
            CMS_LOG(LL_ERROR, "shmat error, error\n");
            return CMS_NOK;
        }        
        /*size = fileSize - SHMMAX*(i+1);
        ptr1 = ptr + SHMMAX;
        i++;*/
    }
    
    return ret;
}


/*
Description:
 cmsXml_allocShareMemory -- Alloc share memmory by file size.   
                                                                          
 INPUT: xmlShmCb  -- xml share memory block.                         
        fileSize  -- the xml file need to parse to share memmory 
        processFlag -- 1, core process which alloc shm really; 0, other process which just attach shm.
                                                                      
 OUTPUT: -1 -- fail                                             
           1  -- success                                                 
           2  -- success but need set shm to readonly                                         
                                                                      
 History:                                                         
     06/18/2020 cms:created                                         
 */
static int cmsXml_allocShareMemory(cmsXmlCb_t *xmlShmCb, uint32_t fileSize, int processFlag, char *cmsFileName){
    key_t keys;
    uint32_t size = 0;
    int shmId, errsv = 0;
    char *ptr = NULL;
    char shmKeyFile[256] = {0};
    int ret=0, i=0;

    CMS_LOG(LL_FLOOD, "fileSize:%x\n", fileSize);
    if ((xmlShmCb == NULL) || (fileSize == 0)){
        return -1;
    }
    size = fileSize;
    if(size > 0){
        if(cmsFileName && strstr(cmsFileName,"restore")){
            sprintf(shmKeyFile,"%s%s_%d", CMS_KEY_DIR, PRE_CONF_FILE_SHM".restore", i);
        }else{
            sprintf(shmKeyFile,"%s%s_%d", CMS_KEY_DIR, PRE_CONF_FILE_SHM, i);
        }

        /*allocate share memory*/
        CMS_LOG(LL_FLOOD, "shmKey:%s\n", shmKeyFile);
        
        ret =CreatShmKeyFile(shmKeyFile);
        keys=ftok(shmKeyFile, 'a');
        errsv = errno;
        CMS_LOG(LL_FLOOD, "keys:%d\n", keys);
        if (keys == -1){
            CMS_LOG(LL_FLOOD, "errno:%x\n", errsv);
            if (errsv == ENOENT){
                ret =CreatShmKeyFile(shmKeyFile);
                keys=ftok(shmKeyFile, 'a');
                CMS_LOG(LL_FLOOD, "get keys again:%d\n", keys);
            }
            else{
                CMS_LOG(LL_ERROR, "rdmInit SHM ftok errno:%x\n", errno);
            }
        }
        shmId = shmget(keys, size, 0666 | S_IRUSR | S_IWUSR); 
        errsv = errno;
        
        if (shmId == -1){    /*fail, no process already create the share memory, create it!!*/
            if (errsv != ENOENT){
                CMS_LOG(LL_ERROR, "shmget error, errno is:%x\n", errsv);
                return -1;
            }
            if(processFlag == 0){
                shmId = shmget (keys, size,IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR | 0666);
                ptr=(void*)shmat (shmId, 0, 0);
                if (allow_direct_write_xmlShm){
                    ret = 1;    /*call by process which define XML_SHM_DIRECT_WRITE*/
                }
                else{
                    ret = 2;    /*call by indirect process which not define XML_SHM_DIRECT_WRITE*/
                }
                xmlShmCb->readOnly = 0;
            }else{
                CMS_LOG(LL_ERROR, "fail, no process already create the share memory!!\n");
                return -1;    
            }        
        }else{/*success*/
            if (allow_direct_write_xmlShm){        
                ptr=(void*)shmat (shmId, 0, 0);
                xmlShmCb->readOnly = 0;
            }else{
                ptr=(void*)shmat (shmId, 0, SHM_RDONLY);
                xmlShmCb->readOnly = 1;
            }
            ret = 0;
        }
        if (ptr < 0){
            CMS_LOG(LL_ERROR, "shmat error, errno\n");
            return -1;
        }
        
        xmlShmCb->baseaddr = ptr;
        xmlShmCb->rootNode = (objectNode_t *)( ptr + sizeof( uint32_t ) * 4 ); /* version, size, used size and string offset. */
        xmlShmCb->OID = 0;
        xmlShmCb->IA = 0;
        xmlShmCb->offset = 0;
        xmlShmCb->shmId = shmId;
        xmlShmCb->shmSize = fileSize;
    }else{
        CMS_LOG(LL_ERROR, "xml share memory error,  %d\n", size);
        ret = -1;
    }
    CMS_LOG(LL_FLOOD, "ret:%x\n", ret);
    
    return ret;
}

/*
Description:
 cmsXml_SaveShmToCmsFile -- save current shm to cms file.         
                                                                        
 INPUT: xmlShmCb    -- xml share memory block.
         xmlFileName -- xml file name. 
         cmsFileName -- cms file name. 
                                                                 
 OUTPUT: CMS_NOK -- fail                                            
         CMS_OK  -- success                                        
                                                                 
 History:                                                         
    06/18/2020 cms:created                                         
*/
static int cmsXml_SaveShmToCmsFile(cmsXmlCb_t *xmlShmCb, char *xmlFileName, char *cmsFileName){
    key_t keys;
    char keyFile[64] = {0};
    int semid = 0;//, result = 0;
    struct sembuf sbEnter[2] = { {0, 0, 0}, {0, 1, SEM_UNDO} };
    struct sembuf sbLeave = {0, -1, SEM_UNDO};  /* set to allocate resource */
    char cmd_str[ 255 ] = { 0 };
    FILE *fp = NULL;
    SRes ret = 0;
    struct stat cmsfileBuf;

    /*create sem*/
    if(xmlFileName != NULL){ 
        snprintf(keyFile, sizeof(keyFile) -1, "%s", CMS_DEF_FILE_ENC);
        keys=ftok(keyFile, 'a');
        
        semid = semget( keys, 1, 0666 );

        if (semid == -1){
            CMS_LOG(LL_ERROR, "semget fail %s, errno is:%x\n", xmlFileName, errno);
            return -1;
        }
        if (semop(semid, &sbEnter[0], sizeof(sbEnter)/sizeof(struct sembuf)) == -1) {
            CMS_LOG(LL_ERROR, "semop fail\n");
            return -1;
        }
    }
    

    /*check cms isExist*/
    if(access(cmsFileName, F_OK)!=0){
        CMS_LOG(LL_FLOOD, "file not exist, create current cms file\n");
        fp = fopen(cmsFileName,"a+"); 
        if(fp==NULL){
            if ((xmlFileName != NULL)&&(semop(semid, &sbLeave, sizeof(sbLeave)/sizeof(struct sembuf)) == -1)) {
                CMS_LOG(LL_ERROR, "semop fail\n");
                return -1;
            }            
            return -1;
        }
        fclose(fp);
    }
    

    /*
    *backup cms before save, avoid reboot or excption when save.
    */
    if ((stat(cmsFileName, &cmsfileBuf) != -1) && (cmsfileBuf.st_size > 0)){    
        sprintf(cmd_str, "cp -r %s %s%s", cmsFileName, "/tmp/", "config.current.cms_bak");
        cms_do_cmd(cmd_str);

        sprintf(cmsFileName, "/tmp/%s", "config.current.cms");
    }

    
    /*save cms file*/
    fp = fopen( cmsFileName, "w");
    if( fp == NULL){
        CMS_LOG(LL_ERROR, "cannot open file cmsFileName :%s\n", cmsFileName);
        if ((xmlFileName != NULL)&&(semop(semid, &sbLeave, sizeof(sbLeave)/sizeof(struct sembuf)) == -1)) {
            CMS_LOG(LL_ERROR, "semop fail\n");
            return -1;
        }                
        return -1;
    }
    
    MySetFileLength(xmlShmCb->shmSize);
    ret = Encode(xmlShmCb->baseaddr, fp);
    fclose(fp);
    
    if ( SZ_OK !=  ret ){

        CMS_LOG(LL_ERROR, "RDM encoder fail!!\n");
        return -1;
    }    

    /*rm cms bak file after save.*/
    //memset(cmd_str, 0, sizeof(cmd_str));
    //sprintf(cmd_str, "rm %s%s", CUR_XML_PATH, "config.current.cms_bak");
    //cms_do_cmd(cmd_str);
    
    /*sem exit*/
    if(xmlFileName != NULL){ 
        if (semop(semid, &sbLeave, sizeof(sbLeave)/sizeof(struct sembuf)) == -1) {
            CMS_LOG(LL_ERROR, "semop fail\n");
            return -1;
        }        
    }    
    
    return CMS_OK;
}


/*
Description:
 cmsXml_checkPreConfig -- check pre config xml.          
                                                                          
 INPUT:  cmsFileName -- cms file name.                                  
                                                                                                                                            
 OUTPUT: 0 -- config.pre.xml is not exist.                                             
          1 -- config.pre.xml is exist, need load to shm.
          2 -- config.pre.xml is exist, have load success, no need load to shm.
          -1 -- error.
                                                                  
 History:                                                         
     08/06/2020 cms:created                                         
*/
#ifdef CUSTOM_PRECONFIG_SUPPORT
static int cmsXml_checkPreConfig(cmsXmlCb_t *xmlShmCb, char *cmsFileName)
{
    struct stat preXmlBuf;
    char preXmlPath[64] = {0};    //pre config path
    char preXmlTimeFile[64] = {0};    //store pre config file time
    char preXmlTime[64] = {0};
    int checkRet = -1;
    FILE *fp = NULL;

    /*
    * init prexml path.
    */
    sprintf(preXmlPath, "%s%s", CUR_XML_PATH, PRE_CONFIG_NAME);
    sprintf(preXmlTimeFile, "%s%s", CUR_XML_PATH, "preConfigTime");
    CMS_LOG(LL_FLOOD, "pre xml path name is %s\n", preXmlPath);

    /*
    * 1, prexmlFlag not exist, have not load, need load, reurn 1.
    * 2, prexmlFlag exist, time info is different with pre xml file. pre xml have load newer by tftp, return 1.
    * 3, prexmlFlag exist, time info is same with pre xml file. pre xml have loader success, return 2.
    */
    if((access(preXmlPath, F_OK)) != -1){

        if((access(preXmlTimeFile, F_OK)) != -1){//have load pre xml, need compare time to check if pre xml have load new file.
            if(stat(preXmlPath, &preXmlBuf) < 0){
                CMS_LOG(LL_ERROR, "stat file %s fail.\n", preXmlPath);
                return -1;
            }

            fp = fopen(preXmlTimeFile, "r");
            if(fp != NULL){
                if(fgets(preXmlTime, 64, fp) == NULL){
                    CMS_LOG(LL_ERROR, "fgets from  file %s fail.\n", preXmlPath);
                    fclose(fp);
                    return -1;
                }
                    
                fclose(fp);

                /*compare time*/
                CMS_LOG(LL_FLOOD, "cur pre xml time: %ld, preXml time store in file: %s\n", preXmlBuf.st_mtime, preXmlTime);
                if(preXmlBuf.st_mtime == atoi(preXmlTime))
                    checkRet = 2;
                else{
                    checkRet = 1;
                    cms_do_cmd("rm -rf /userconfig/config/config.current.*");
                }
            }else{
                CMS_LOG(LL_ERROR, "fopen file %s fail.\n", preXmlPath);
                return -1;
            }
        }else{//have not load pre xml
            checkRet = 1;
        }
    }
    else{//pre xml is not exist.
        checkRet = 0;
    }

    return checkRet;

}
#endif

/*
Description:
 cmsXml_InitNodeMemory -- init share memmory xml content.          
                                                                          
 INPUT: xmlShmCb  -- xml share memory block.                         
          xmlFileName -- current xml file name. 
          preDefaultXml -- default xml file name.
          cmsFileName -- cms file name.                                  
          userConfigFlag -- flag for user Customized.             
                                                                  
                                                                  
 OUTPUT: CMS_NOK -- fail                                             
           CMS_OK  -- success                                          
                                                                  
 History:                                                         
     06/18/2020 cms:created                                         
*/
static int cmsXml_InitNodeMemory(
    char *cmsFileName, 
    char *xmlFileName, 
    char *preDefaultXml, 
    cmsXmlCb_t *xmlShmCb, 
    int userConfigFlag){
    
    FILE *fp = NULL;
    char *ptr = NULL;
    int ret = 0, needLoadXml = 0;
    //char cmd_str[255] = {0};
    //int result;        
    int retValue = 0;
    char pathDefaultXml[256] = {0};
    char curCmsFile[256] = {0};
    struct stat buf1, buf2;
    uint32_t file_size = 0;    /*file size*/
    int restoreFlag = 0;
    char cmsLogStr[256] = {0};

    if((cmsFileName == NULL) || (xmlFileName == NULL) || (xmlShmCb == NULL))
        return -1;
    
    /*
    *check pre xml.
    */
#ifdef CUSTOM_PRECONFIG_SUPPORT
    int preCheckRet = -1;
    struct stat preXmlTime;
    char cmdStr[128] = {0};

    preCheckRet = cmsXml_checkPreConfig(xmlShmCb, cmsFileName);
    CMS_LOG(LL_FLOOD, "cms xml check pre config xml ret: %d", preCheckRet);
    if(preCheckRet == -1)
        return CMS_NOK;
#endif

    CMS_LOG(LL_FLOOD,"the cms file name %s\n", cmsFileName);
    if(cmsFileName && strstr(cmsFileName,"restore")){
        CMS_LOG(LL_FLOOD,"it's for restore default, not change it");
        needLoadXml = 1;
        restoreFlag = 1;
    }else{
        /*
        **check timestamp between config.current.cms and config.current.xml
        */
        if (stat(cmsFileName, &buf1) < 0){
            /*
            **no config.current.cms, it's firmware upgrade or upgrade configuration file. 
            **Del current cms file when fw upgrade or configuation file upgrade
            */
            CMS_LOG(LL_FLOOD, "No config.current.cms, it's firmware upgrade or conf upgrade.");

            memset(cmsFileName, 0, 256);
            sprintf(cmsFileName, "%s", CMS_PRE_FILE_SHM);
            needLoadXml = 1;            /*current cms is older than current xml, load current xml to shm*/
            stat(cmsFileName, &buf1);    /*get config.cms's size*/

            CMS_LOG(LL_FLOOD,"cms default file name %s, no config.current.cms", cmsFileName);
        }
        else{
            /*
            **compare config.current.cms and config.current.xml time
            */
            stat(xmlFileName, &buf2);
            if ((buf1.st_mtime - buf2.st_mtime) <= 0){
                CMS_LOG(LL_FLOOD, "%s(): config.current.cms is older, need load config.current.xml and cms file is config.default.cms", __FUNCTION__);

                memset(cmsFileName, 0, 256);
                sprintf(cmsFileName, "%s", CMS_PRE_FILE_SHM);
                needLoadXml = 1;            /*current cms is older than current xml, load current xml to shm*/
                stat(cmsFileName, &buf1);    /*get config.cms's size*/

                CMS_LOG(LL_FLOOD,"cms default file name %s, no config.current.cms", cmsFileName);
            }
        }
    }


    /*
    **get .cms file size, then alloc shmSize.
    */
    CMS_LOG(LL_FLOOD, "Reading %s ...", cmsFileName);
    fp = fopen( cmsFileName, "r");
    if( fp == NULL){
        CMS_LOG(LL_ERROR, "cannot open file cmsFileName:%s", cmsFileName);
        return CMS_NOK;
    }else{
        file_size = GetUnpackSize(fp);
        fclose(fp);
        CMS_LOG(LL_FLOOD, "cms file_size %x", file_size);
    }
    
    /*fileSize is 1, means reboot or exception when save, cms size is 0.*/
    /*if(file_size == 1 && restoreFlag == 0){
        strcpy(cmsFileName, "/userconfig/config/config.current.cms_bak");
        fp = fopen( cmsFileName, "r");
        if(fp){
            file_size = GetUnpackSize(fp);
            fclose(fp);
        }
    }*/

    /*
    **alloc shm size by .cms file size.
    */
    if ((ret = cmsXml_allocShareMemory(xmlShmCb, file_size, 0, cmsFileName)) > 0){

        /*need to add some check here, only need to read the file at first time create share memory!!*/
        CMS_LOG(LL_FLOOD,"cms file name %s, decode config.current.cms to file", cmsFileName);
        ptr = xmlShmCb->baseaddr;
        fp = fopen( cmsFileName, "r");
        if( fp == NULL){
            CMS_LOG(LL_ERROR, "cannot open file cmsFileName:%s", cmsFileName);
            return CMS_NOK;
        }        
        Decode(fp, ptr);
        fclose(fp);

        /*load ROM to RAM*/
        if (needLoadXml == 1){
                if(restoreFlag == 0){
                /* 
                *Config loading order when failed:
                * config.current.enc->config.curBak.xml-> config.preDownload.xml->config.province.enc(province default xml)->config.default.enc -> config.default.cms 
                */

                /*when first time load from old fw to new with pre & backup file, md5sum file will not exist, if return POK, load current file*/
                CMS_LOG(LL_PRINT|LL_SYSLOG, "Loading %s", xmlFileName);
                memset(cmsLogStr, 0, sizeof(cmsLogStr));
                sprintf(cmsLogStr, "Loading %s\n", xmlFileName);
                cms_log_record(cmsLogStr);
                retValue = cmsXml_loadRomToRam(xmlShmCb, xmlFileName);
                if(retValue!=0){
                    if (userConfigFlag) {
                        return CMS_NOK;
                    }
        
                    CMS_LOG(LL_ERROR|LL_SYSLOG, "Load %s fail!!! Reloading %s ...", xmlFileName, CMS_BACKUP_CONFIG_FILE);
                    memset(cmsLogStr, 0, sizeof(cmsLogStr));
                    sprintf(cmsLogStr, "Load %s fail!!! Reloading %s ...\n", xmlFileName, CMS_BACKUP_CONFIG_FILE);
                    cms_log_record(cmsLogStr);
                    retValue = cmsXml_loadRomToRam(xmlShmCb, CMS_BACKUP_CONFIG_FILE);
                    if(retValue!=0){
            #ifdef CUSTOM_PRECONFIG_SUPPORT
                        preCheckRet = 1;
            #endif        
                        /*load pre download xml*/
                        CMS_LOG(LL_ERROR|LL_SYSLOG, "Load %s fail!!! Reloading %s ...", CMS_BACKUP_CONFIG_FILE, CMS_PRE_CONFIG_FILE);
                        memset(cmsLogStr, 0, sizeof(cmsLogStr));
                        sprintf(cmsLogStr, "Load %s fail!!! Reloading %s ...\n", CMS_BACKUP_CONFIG_FILE, CMS_PRE_CONFIG_FILE);
                        cms_log_record(cmsLogStr);
                        retValue = cmsXml_loadRomToRam(xmlShmCb, CMS_PRE_CONFIG_FILE);
                        if(retValue!=0){
                            /*load province default xml*/
                            if(preDefaultXml != NULL){
                                if(access(preDefaultXml, F_OK) == -1){
                                    CMS_LOG(LL_ERROR, "the default xml path(%s) is not exist!!!", preDefaultXml);
                                    snprintf(pathDefaultXml, sizeof(pathDefaultXml) -1, "%s", CMS_DEF_FILE_ENC);
                                }else{
                                    strcpy(pathDefaultXml, preDefaultXml);
                                }
                            }else{
                                CMS_LOG(LL_ERROR, "the default xml path is NULL!!!");
                                snprintf(pathDefaultXml, sizeof(pathDefaultXml) -1, "%s", CMS_DEF_FILE_ENC);
                            }

                            CMS_LOG(LL_ERROR|LL_SYSLOG, "Load %s fail!!! Reloading %s ...", CMS_PRE_CONFIG_FILE, pathDefaultXml);
                            memset(cmsLogStr, 0, sizeof(cmsLogStr));
                            sprintf(cmsLogStr, "Load %s fail!!! Reloading %s ...\n", CMS_PRE_CONFIG_FILE, pathDefaultXml);
                            cms_log_record(cmsLogStr);
                            retValue = cmsXml_loadRomToRam(xmlShmCb, pathDefaultXml);
                            if(retValue!=0){
                                CMS_LOG(LL_ERROR|LL_SYSLOG, "Load %s fail!!! Reloading %s ...", pathDefaultXml, cmsFileName);
                                memset(cmsLogStr, 0, sizeof(cmsLogStr));
                                sprintf(cmsLogStr, "Load %s fail!!! Reloading %s ...\n", pathDefaultXml, cmsFileName);
                                cms_log_record(cmsLogStr);
                                /*reload to default config.default.cms*/
                                fp = fopen(cmsFileName, "r");
                                if(fp == NULL){
                                    CMS_LOG(LL_ERROR, "Cannot open %s!!!", cmsFileName);
                                    return CMS_NOK;
                                }        
                                Decode(fp, ptr);
                                fclose(fp);
                                CMS_LOG(LL_ERROR|LL_SYSLOG, "Load %s OK!!!", cmsFileName);
                                memset(cmsLogStr, 0, sizeof(cmsLogStr));
                                sprintf(cmsLogStr, "Load %s OK!!!\n", cmsFileName);
                                cms_log_record(cmsLogStr);
                            }else{
                                CMS_LOG(LL_PRINT|LL_SYSLOG, "Load %s OK!!!", pathDefaultXml);
                                memset(cmsLogStr, 0, sizeof(cmsLogStr));
                                sprintf(cmsLogStr, "Load %s OK!!!\n", pathDefaultXml);
                                cms_log_record(cmsLogStr);
                            }
                        }
                    }
                }else{
                    CMS_LOG(LL_PRINT|LL_SYSLOG, "Load %s OK!!!", xmlFileName);
                    memset(cmsLogStr, 0, sizeof(cmsLogStr));
                    sprintf(cmsLogStr, "Load %s OK!!!\n", xmlFileName);
                    cms_log_record(cmsLogStr);
                    if (userConfigFlag) {
                        return CMS_OK;
                    }
                }

                CMS_LOG(LL_INFO, "rdmGetUsedShmSize( xmlShmCb )=%u", cmsXmlGetUsedShmSize( xmlShmCb ) );
            
            }
            else{
        #ifdef CUSTOM_PRECONFIG_SUPPORT
                preCheckRet = 1;
        #endif
                if(access(CMS_PRE_CONFIG_FILE, F_OK) == -1){
                    if(preDefaultXml != NULL){
                        if(access(preDefaultXml, F_OK) == -1){
                            CMS_LOG(LL_ERROR, "the default xml path(%s) is not exist!!!", preDefaultXml);
                            snprintf(pathDefaultXml, sizeof(pathDefaultXml) - 1, "%s", CMS_DEF_FILE_ENC);
                        }else{
                            strcpy(pathDefaultXml, preDefaultXml);
                        }
                    }else{
                        CMS_LOG(LL_ERROR, "the default xml path is NULL!!!");
                        snprintf(pathDefaultXml, sizeof(pathDefaultXml) - 1, "%s", CMS_DEF_FILE_ENC);
                    }
                }else{
                    strcpy(pathDefaultXml, CMS_PRE_CONFIG_FILE);
                }
    
                CMS_LOG(LL_PRINT|LL_SYSLOG,  "Loading %s ...", pathDefaultXml);
                memset(cmsLogStr, 0, sizeof(cmsLogStr));
                sprintf(cmsLogStr, "Loading %s ...\n", pathDefaultXml);
                cms_log_record(cmsLogStr);
                retValue = cmsXml_loadRomToRam(xmlShmCb, pathDefaultXml);
                if(retValue!=0){
                    CMS_LOG(LL_ERROR|LL_SYSLOG, "Load %s fail!!! Reloading %s ...", pathDefaultXml, cmsFileName);

                    /*reload to default config.default.cms*/
                    fp = fopen(cmsFileName, "r");
                    if(fp == NULL){
                        CMS_LOG(LL_ERROR, "Cannot open %s!!!", cmsFileName);
                        return CMS_NOK;
                    }        
                    Decode(fp, ptr);
                    fclose(fp);
                    CMS_LOG(LL_PRINT|LL_SYSLOG, "Load %s OK!!!", cmsFileName);
                    memset(cmsLogStr, 0, sizeof(cmsLogStr));
                    sprintf(cmsLogStr, "Load %s OK!!!\n", cmsFileName);
                    cms_log_record(cmsLogStr);
                }else{
                    CMS_LOG(LL_PRINT|LL_SYSLOG, "Load %s OK!!!", pathDefaultXml);
                    memset(cmsLogStr, 0, sizeof(cmsLogStr));
                    sprintf(cmsLogStr, "Load %s OK!!!\n", pathDefaultXml);
                    cms_log_record(cmsLogStr);
                }
            }

            
            /*
            **touch config.current.xml to fix timestamp problem
            **when board's systime is not right after restore default, the timestamp of config.current.xml (ex. 00:05)
            **is newer than config.currrent.cms (ex. 00:00, becuase board's time is always start from 00:00)
            **so cms will reload config.current.xml everytime, touch config.current.xml before config.current.cms
            */
            #if 0
            CMS_LOG(LL_FLOOD, "touch config.xml=%s\n", xmlFileName);
            if(access(xmlFileName, F_OK) == -1){/*first boot time, there is no content in config.current.xml if not config something*/
                memset(cmd_str, 0, sizeof(cmd_str));
                sprintf(cmd_str, "cp -r %s %s", CMS_DEF_FILE_XML, CMS_CUR_FILE_XML);
                cms_do_cmd(cmd_str);
            }
            memset(cmd_str, 0, sizeof(cmd_str));
            sprintf(cmd_str, "touch %s", xmlFileName);
            result = cms_do_cmd(cmd_str);
            if (result != 0){
                CMS_LOG(LL_WARNING, "system cmd for touch config.xml fail\n");
            }
            #endif

#ifdef RDM_LATEST_SUPPORT
            /*if shmInit load RomToRam means 1. f/w upgrade, no current.cms 2. config file upgrade
            then we save config.current.cms here to accelerate shmInit speed afterward!*/
            
            sprintf(curCmsFile, "/tmp/%s", "config.current.cms");
            CMS_LOG(LL_FLOOD, "save %s\n", curCmsFile);
            cmsXml_SaveShmToCmsFile(xmlShmCb, NULL, curCmsFile);
#endif        
        
        }
        
#ifdef CUSTOM_PRECONFIG_SUPPORT
        sprintf(pathDefaultXml, "%s%s",CUR_XML_PATH, "config.pre.xml");
        sprintf(curCmsFile, "/tmp/%s", "config.current.cms");

        if((preCheckRet == 1) && (stat(pathDefaultXml, &buf1) !=  -1)){
            memset(cmsLogStr, 0, sizeof(cmsLogStr));
            sprintf(cmsLogStr, "Loading %s.\n", pathDefaultXml);
            cms_log_record(cmsLogStr);
            retValue = cmsXml_loadRomToRam(xmlShmCb, pathDefaultXml);
            if(retValue != 0){
                CMS_LOG(LL_ERROR, "load pre xml fail.\n");
                memset(cmsLogStr, 0, sizeof(cmsLogStr));
                sprintf(cmsLogStr, "load pre xml <%s> fail.\n", pathDefaultXml);
                cms_log_record(cmsLogStr);
            }else{
                cmsXml_SaveShmToCmsFile(xmlShmCb, NULL, curCmsFile);
                
                stat(pathDefaultXml, &preXmlTime);
                sprintf(cmdStr, "echo %ld > %s%s", preXmlTime.st_mtime, CUR_XML_PATH, "preConfigTime");
                cms_do_cmd(cmdStr);
            }
        }
                            
#endif

        /*
        **a indirect access process init the shm, reset to read only
        */
        if (ret == 2){    
            if (cmsXml_setShareMemoryReadOnly(xmlShmCb, file_size, cmsFileName) < 0){
                CMS_LOG(LL_ERROR, "cmsXml_SetShareMemoryRead() fail\n");
                return CMS_NOK;
            }
                
        }
    }

    if (ret < 0){
        return CMS_NOK;
    }
    
    return CMS_OK;
}


/*
Description:
 cmsXml_attachXmlShm -- attach share memmory xml to current process.          
                                                                          
 INPUT: xmlShmCb  -- xml share memory block.                               
          cmsFileName -- cms file name.                                                                                                        
                                                                  
 OUTPUT: CMS_NOK -- fail                                             
           CMS_OK  -- success                                          
                                                                  
 History:                                                         
     06/18/2020 cms:created                                         
*/
static int cmsXml_attachXmlShm(cmsXmlCb_t *xmlShmCb, char *cmsFileName)
{
    
    uint32_t file_size = 0;
    FILE  *fp = NULL;
    int ret = 0;

    /*read cms file size*/
    CMS_LOG(LL_FLOOD, "Reading %s ...\n", cmsFileName);
    fp = fopen( cmsFileName, "r");
    if( fp == NULL){
        CMS_LOG(LL_ERROR, "cannot open file cmsFileName:%s, error code %d(%s)\n", cmsFileName, errno, strerror(errno));
        return -1;
    }
    
    file_size = GetUnpackSize(fp);
    fclose(fp);

    /*fileSize is 1, means reboot or exception when save, cms size is 0.*/
    if(file_size == 1){
        sprintf(cmsFileName, "%s", CMS_PRE_FILE_SHM);
        fp = fopen( cmsFileName, "r");
        if(fp){
            file_size = GetUnpackSize(fp);
            fclose(fp);
        }
    }

    /*attach shm to current process*/
    CMS_LOG(LL_FLOOD, "xmlShm file_size %x\n", file_size);
    if ((ret = cmsXml_allocShareMemory(xmlShmCb, file_size, 1, cmsFileName)) < 0){
        CMS_LOG(LL_ERROR, "attach share memory fail:%s\n", cmsFileName);
        return -1;        
    }

    return 0;
}


/*
Description:
 cms_xmlShmAllocate -- init share memmory xml content.         
                                                                        
 INPUT: None         
                                                                 
 OUTPUT: NULL -- fail                                            
         cmsXmlCb_t pointer  -- success                                        
                                                                 
 History:                                                         
    06/18/2020 cms:created                                         
*/
cmsXmlCb_t *cms_xmlShmAllocate(char *preDefaultXml){
    cmsXmlCb_t    *xmlShmCb = NULL;
    key_t keys;
    char keyFile[64] = {0};
    int semid, ret = 0, len = 0, errsv = 0;
    char xmlFileName[256] = {0};            /*config.current.xml file path*/
    char cmsFileName[256] = {0};            /*config.current.cms file path*/
    struct sembuf sbEnter[2] = { {0, 0, 0}, {0, 1, SEM_UNDO} };
    struct sembuf sbLeave = {0, -1, SEM_UNDO};      /* set to allocate resource */
    sigset_t newmask, oldmask;

    /*init xml path name*/
    sprintf(xmlFileName, "%s", CMS_CUR_FILE_ENC);
    sprintf(cmsFileName, "/tmp/%s" , "config.current.cms");
    
    /*create sem key*/
    sprintf(keyFile, "%s", CMS_DEF_FILE_ENC);
    keys=ftok(keyFile, 'a');
    errsv = errno;
    if (keys == -1){
        if (errsv == ENOENT){
            char *newKeyFile = "/tmp/cmsAllocKey";
            ret =CreatShmKeyFile(newKeyFile);
            if(ret == CMS_NOK){
                CMS_LOG(LL_ERROR, "create key file fail by %s", newKeyFile);
                return NULL;
            }
            keys=ftok(newKeyFile, 'a');
        }
        else{
            CMS_LOG(LL_ERROR, "sem ftok errno:%x", errno);
        }
    }

    /*get/creat sem*/
    semid = semget( keys, 1, 0666 );
    errsv = errno;
    if (semid == -1){
        if (errsv != ENOENT){
            CMS_LOG(LL_ERROR, "sem semget errno:%x", errno);
            return NULL;
        }
        semid = semget( keys, 1, 0666 | IPC_CREAT | IPC_EXCL );

        if (semid != -1){
            CMS_LOG(LL_FLOOD, "create sem success");
        }
        else{
            CMS_LOG(LL_ERROR, "create sem fail");
            return NULL;
        }
          /* initialize semaphore #0 to 1: */
         if (semctl(semid, 0, SETVAL, 0) == -1) {
            CMS_LOG(LL_ERROR, "semctl fail");
            return NULL;
         }
            
    }

    sigemptyset(&newmask);
    sigfillset(&newmask);
    /* block all signals */
    sigprocmask(SIG_SETMASK, &newmask, &oldmask);

    if (semop(semid, &sbEnter[0], sizeof(sbEnter)/sizeof(struct sembuf)) == -1) { 
        errsv = errno;
        CMS_LOG(LL_ERROR, "semop fail at sem enter, error code %d", errsv);
        sigprocmask(SIG_SETMASK, &oldmask, NULL);
        return NULL;
    }

    /*alloc memory for xml share memory block*/
    xmlShmCb = malloc(sizeof(cmsXmlCb_t));
    if (xmlShmCb == NULL){
        CMS_LOG(LL_ERROR, "xmlShmCb malloc fail");
        return NULL;
    }
    memset(xmlShmCb, 0, sizeof(cmsXmlCb_t));

    /*create cms mutex*/
#ifdef XML_LOCK_SUPPORT
    cmsMutexPack_t *cmsMutex = create_mutex_package(xmlShmCb);
    if(cmsMutex == NULL)
        xmlShmCb->cmsMutex = NULL;
    else
        xmlShmCb->cmsMutex = cmsMutex;
#endif

    /*
    **allocate share memory for tree node and data and 
    **read the tree node and data from file.
    */
    if (cmsXml_InitNodeMemory(cmsFileName, xmlFileName, preDefaultXml, xmlShmCb, 0) < 0){
        CMS_LOG(LL_ERROR, "xml shm error");
        free(xmlShmCb);
        if (semop(semid, &sbLeave, sizeof(sbLeave)/sizeof(struct sembuf)) == -1) {
            errsv = errno;
            CMS_LOG(LL_ERROR, "semop fail at init share memory, error:%x", errsv);
            sigprocmask(SIG_SETMASK, &oldmask, NULL);
            return NULL;
        }        
        sigprocmask(SIG_SETMASK, &oldmask, NULL);
        return NULL;
    }

    if (semop(semid, &sbLeave, sizeof(sbLeave)/sizeof(struct sembuf)) == -1) {
        errsv = errno;
        CMS_LOG(LL_ERROR, "semop fail at sem level:%x", errsv);        
        sigprocmask(SIG_SETMASK, &oldmask, NULL);
        return NULL;
    }
    sigprocmask(SIG_SETMASK, &oldmask, NULL);
    
    /*
    **alloctae memory for xmlFile name and cmsFile name
    */
    len = strlen(cmsFileName);
    xmlShmCb->cmsFileName = calloc((len+1), 1);    /*1 is for '\0'*/
    if (xmlShmCb->cmsFileName == NULL){
        cms_xmlShmDeInit(&xmlShmCb);
        return NULL;
    }
    strncpy(xmlShmCb->cmsFileName, cmsFileName, len);
    
    len = strlen(xmlFileName);
    xmlShmCb->xmlFileName = calloc((len+1), 1);
    if (xmlShmCb->xmlFileName == NULL){
        cms_xmlShmDeInit(&xmlShmCb);
        return NULL;
    }    
    strncpy(xmlShmCb->xmlFileName, xmlFileName, len);
    
    CMS_LOG(LL_FLOOD, "finish xmlShmCb:%"PRIxPTR, (uintptr_t)xmlShmCb);
    return xmlShmCb;
}

/*
Description:
 cms_xmlShmDestory -- destory share memmory of xml.         
                                                                        
 INPUT: xmlShmCb -- xml share memory block.         
                                                                 
 OUTPUT: CMS_NOK -- fail                                            
         CMS_OK  -- success                                        
                                                                 
 History:                                                         
    06/18/2020 cms:created                                         
*/
int cms_xmlShmDestory(cmsXmlCb_t *xmlShmCb){
    char *shmPtr = NULL;
    char keyFile[64] = {0};
    key_t keys;
    int semid = 0;
    struct sembuf sbEnter[2] = { {0, 0, 0}, {0, 1, SEM_UNDO} };
    struct sembuf sbLeave = {0, -1, SEM_UNDO};      /* set to allocate resource */
    int errsv = 0, ret = 0;

    if (NULL == xmlShmCb){
        CMS_LOG(LL_ERROR, "xmlShmCb is NULL.\n");
        return CMS_OK;
    }
    /*create sem*/
    snprintf(keyFile, sizeof(keyFile) - 1, "%s", CMS_DEF_FILE_ENC);
    keys=ftok(keyFile, 'a');
    errsv = errno;
    if (keys == -1){
        if (errsv == ENOENT){
            char *newKeyFile = "/tmp/cmsDeInitKey";
            ret =CreatShmKeyFile(newKeyFile);
            if(ret == CMS_NOK){
                CMS_LOG(LL_ERROR, "create key file fail by %s", newKeyFile);
                return CMS_NOK;
            }
            keys=ftok(newKeyFile, 'a');
        }
        else{
            CMS_LOG(LL_ERROR, "sem ftok errno:%x", errno);
        }
    }
    
    semid = semget( keys, 1, 0666 );
    if (semid == -1){
        CMS_LOG(LL_ERROR, "semget fail, errno is:%x", errno);
        return CMS_NOK;
    }
    if (semop(semid, &sbEnter[0], sizeof(sbEnter)/sizeof(struct sembuf)) == -1) {
        CMS_LOG(LL_ERROR, "semop fail, errno %d(%s)", errno, strerror(errno));
        return CMS_NOK;
    }

    shmPtr = xmlShmCb->baseaddr;

    /*deatch share memory*/
    ret = shmdt(shmPtr);
    if (ret < 0){
        CMS_LOG(LL_ERROR, "shmdt error %d(%s), ret %d", errno, strerror(errno), ret);
    }

    /*remove shm, and free memory*/
    shmctl(xmlShmCb->shmId, IPC_RMID, 0);
    if (xmlShmCb->cmsFileName)
        free(xmlShmCb->cmsFileName);
    if (xmlShmCb->xmlFileName)
        free(xmlShmCb->xmlFileName);
    free(xmlShmCb);

    /*destory cms mutex*/
#if 0//def XML_LOCK_SUPPORT
    destory_mutex_package(xmlShmCb);
#endif
    if (semop(semid, &sbLeave, sizeof(sbLeave)/sizeof(struct sembuf)) == -1) {
        CMS_LOG(LL_ERROR, "semop fail, errno %d(%s)", errno, strerror(errno));
        return CMS_NOK;
    }

    return CMS_OK;
}


/*
Description:
 cms_xmlShmInit -- init share memmory xml, attach shm to current process.         
                                                                        
 INPUT: None         
                                                                 
 OUTPUT: NULL -- fail                                            
         cmsXmlCb_t pointer -- success                                        
                                                                 
 History:                                                         
    06/18/2020 cms:created                                         
*/
cmsXmlCb_t *cms_xmlShmInit(){
    cmsXmlCb_t    *xmlShmCb = NULL;
    int len = 0;
    char xmlFileName[256] = {0};            /*config.current.xml file path*/
    char cmsFileName[256] = {0};            /*config.current.cms file path*/
    key_t keys;
    char keyFile[64] = {0};
    int semid = 0;
    struct sembuf sbEnter[2] = { {0, 0, 0}, {0, 1, SEM_UNDO} };
    struct sembuf sbLeave = {0, -1, SEM_UNDO};      /* set to allocate resource */
    int errsv = 0, ret = 0;

    /*create sem*/
    snprintf(keyFile, sizeof(keyFile) - 1, "%s", CMS_DEF_FILE_ENC);
    keys=ftok(keyFile, 'a');
    errsv = errno;
    if (keys == -1){
        if (errsv == ENOENT){
            char *newKeyFile = "/tmp/cmsInitKey";
            ret =CreatShmKeyFile(newKeyFile);
            if(ret == CMS_NOK){
                CMS_LOG(LL_ERROR, "create key file fail by %s", newKeyFile);
                return NULL;
            }
            keys=ftok(newKeyFile, 'a');
        }
        else{
            CMS_LOG(LL_ERROR, "sem ftok errno:%x", errno);
        }
    }
    
    semid = semget( keys, 1, 0666 );

    if (semid == -1){
        CMS_LOG(LL_ERROR, "semget fail %s, errno is:%x", xmlFileName, errno);
        return NULL;
    }
    if (semop(semid, &sbEnter[0], sizeof(sbEnter)/sizeof(struct sembuf)) == -1) {
        CMS_LOG(LL_ERROR, "semop fail, errno %d(%s)", errno, strerror(errno));
        return NULL;
    }

    /*init xml path name*/
    sprintf(xmlFileName, "%s", CMS_CUR_FILE_ENC);
    sprintf(cmsFileName, "/tmp/%s", "config.current.cms");
    if(access(cmsFileName, F_OK) == -1){
        memset(cmsFileName, 0, 256);
        sprintf(cmsFileName, "%s", CMS_PRE_FILE_SHM);
    }

    xmlShmCb = malloc(sizeof(cmsXmlCb_t));
    if (xmlShmCb == NULL){
        CMS_LOG(LL_ERROR, "xmlShmCb malloc fail");
        goto error;
    }
    memset(xmlShmCb, 0, sizeof(cmsXmlCb_t));
    
    /*allocate share memory for tree node and data and read the tree node and data from file*/
    if (cmsXml_attachXmlShm(xmlShmCb, cmsFileName) < 0){
        CMS_LOG(LL_ERROR, "xml shm init error.");
        free(xmlShmCb);
        goto error;
    }

    /*alloctae memory for cms file name and xml file name.*/
    len = strlen(cmsFileName);
    xmlShmCb->cmsFileName = calloc((len+1), 1);    /*1 is for '/0*/
    if (xmlShmCb->cmsFileName == NULL){
        cms_xmlShmDeInit(&xmlShmCb);
        goto error;
    }
    strncpy(xmlShmCb->cmsFileName, cmsFileName, len);

    len = strlen(xmlFileName);
    xmlShmCb->xmlFileName = calloc((len+1), 1);
    if (xmlShmCb->xmlFileName == NULL){
        cms_xmlShmDeInit(&xmlShmCb);
        goto error;
    }    
    strncpy(xmlShmCb->xmlFileName, xmlFileName, len);

#ifdef XML_LOCK_SUPPORT
    cmsMutexPack_t *cmsMutex = create_mutex_package(xmlShmCb);
    if(cmsMutex == NULL)
        xmlShmCb->cmsMutex = NULL;
    else
        xmlShmCb->cmsMutex = cmsMutex;
#endif

    CMS_LOG(LL_FLOOD, "finish xmlShmCb:%"PRIxPTR, (uintptr_t)xmlShmCb);
    if (semop(semid, &sbLeave, sizeof(sbLeave)/sizeof(struct sembuf)) == -1) {
        CMS_LOG(LL_ERROR, "semop fail, errno %d(%s)", errno, strerror(errno));
        return NULL;
    }
    return xmlShmCb;

error:
    if (semop(semid, &sbLeave, sizeof(sbLeave)/sizeof(struct sembuf)) == -1) {
        CMS_LOG(LL_ERROR, "semop fail, errno %d(%s)", errno, strerror(errno));
        return NULL;
    }
    return NULL;
}


/*
Description:
 cms_xmlShmDeInit -- dattach shm from current process.         
                                                                        
 INPUT: xmlShmCb -- share memory control block.         
                                                                 
 OUTPUT: CMS_NOK -- fail                                            
         CMS_OK  -- success                                        
                                                                 
 History:                                                         
    06/18/2020 cms:created                                         
*/
int cms_xmlShmDeInit(cmsXmlCb_t **xmlShmCb){
    char *shmPtr = NULL;
    //uint32_t shmSize = 0, size = 0;    /*share memory size*/
    int ret = -1;
    key_t keys;
    char keyFile[64] = {0};
    int semid = 0;
    struct sembuf sbEnter[2] = { {0, 0, 0}, {0, 1, SEM_UNDO} };
    struct sembuf sbLeave = {0, -1, SEM_UNDO};      /* set to allocate resource */
    int errsv = 0;

    if((*xmlShmCb) == NULL){
        return CMS_NOK;
    }
    
    /*create sem*/
    snprintf(keyFile, sizeof(keyFile) - 1, "%s", CMS_DEF_FILE_ENC);
    keys=ftok(keyFile, 'a');
    errsv = errno;
    if (keys == -1){
        if (errsv == ENOENT){
            char *newKeyFile = "/tmp/cmsDeInitKey";
            ret =CreatShmKeyFile(newKeyFile);
            if(ret == CMS_NOK){
                CMS_LOG(LL_ERROR, "create key file fail by %s", newKeyFile);
                return CMS_NOK;
            }
            keys=ftok(newKeyFile, 'a');
        }
        else{
            CMS_LOG(LL_ERROR, "sem ftok errno:%x", errno);
        }
    }
    
    semid = semget( keys, 1, 0666 );

    if (semid == -1){
        CMS_LOG(LL_ERROR, "semget fail, errno is:%x", errno);
        return CMS_NOK;
    }
    if (semop(semid, &sbEnter[0], sizeof(sbEnter)/sizeof(struct sembuf)) == -1) {
        CMS_LOG(LL_ERROR, "semop fail, errno %d(%s)", errno, strerror(errno));
        return CMS_NOK;
    }

    //shmSize = (*xmlShmCb)->shmSize;
    shmPtr = (*xmlShmCb)->baseaddr;
    
    /*size = shmSize;
    if(size > 0){
        if(size > SHMMAX){
            size = SHMMAX;
        }*/
        ret = shmdt(shmPtr);
        if (ret < 0){
            CMS_LOG(LL_ERROR, "shmdt error %d(%s), ret %d", errno, strerror(errno), ret);
        }
        /*if(size == SHMMAX){
            size = shmSize - SHMMAX*(i+1);
        }else{
            size = 0;
        }
        shmPtr += SHMMAX;
        i++;
    }*/
    
    if ((*xmlShmCb)->cmsFileName){
        free((*xmlShmCb)->cmsFileName);
        (*xmlShmCb)->cmsFileName = NULL;
    }
    if ((*xmlShmCb)->xmlFileName){
        free((*xmlShmCb)->xmlFileName);
        (*xmlShmCb)->xmlFileName = NULL;
    }

#ifdef XML_LOCK_SUPPORT
    if((*xmlShmCb)->lockShmAddr != NULL){
        shmdt((*xmlShmCb)->lockShmAddr);
    }
#endif

    free((*xmlShmCb));
    (*xmlShmCb) = NULL;

    CMS_LOG(LL_FLOOD, "xmlshm deinit success.");
    
    if (semop(semid, &sbLeave, sizeof(sbLeave)/sizeof(struct sembuf)) == -1) {
        CMS_LOG(LL_ERROR, "semop fail, errno %d(%s)", errno, strerror(errno));
        return CMS_NOK;
    }
    return CMS_OK;
}

int cmsXml_saveToFlash(cmsXmlCb_t *xmlShmCb)
{
    int ret = CMS_OK;
    char cmd[128] = {0};
    if(xmlShmCb == NULL)
        return CMS_NOK;
    
    XML_LOCK(&xmlShmCb->cmsMutex->lock);
    if(cmsXml_SaveRamToRom(xmlShmCb, xmlShmCb->xmlFileName) < 0){
        XML_UNLOCK(&xmlShmCb->cmsMutex->lock);
        CMS_LOG(LL_ERROR, "ERROR !!! Save %s fail !!!", xmlShmCb->xmlFileName);
        return CMS_NOK;
    }

    CMS_LOG(LL_PRINT, "----%s %s", CMS_CUR_FILE_XML, CMS_CUR_FILE_ENC);
    if(cmsXml_openSSL_Enc(CMS_CUR_FILE_XML, CMS_CUR_FILE_ENC) == CMS_OK){
        CMS_LOG(LL_DEBUG, "enc xml success after save.");
        snprintf(cmd, sizeof(cmd) - 1, "md5sum %s > %s", CMS_CUR_FILE_ENC, CMS_CUR_ENC_CRC);
        cms_do_cmd(cmd);
    }else{
        CMS_LOG(LL_ERROR, "enc xml fail after save!!!");
        ret = CMS_NOK;
    }
    
    XML_UNLOCK(&xmlShmCb->cmsMutex->lock);

    return ret;
}

int cmsXml_saveToBak(cmsXmlCb_t *xmlShmCb)
{
    int ret = CMS_OK;
    if(xmlShmCb == NULL)
        return CMS_NOK;
    
    XML_LOCK(&xmlShmCb->cmsMutex->lock);
    
    /*save current config to backup partition*/
    if(access(CMS_CUR_FILE_XML, F_OK) == -1){
        if(cmsXml_openSSL_Dec(CMS_CUR_FILE_ENC, CMS_CUR_FILE_XML) == CMS_NOK){
            ret = CMS_NOK;
            goto exit;
        }
    }

#ifdef CMS_SUPPORT_YL
    if(lib_sys_saveBackupConfig(CMS_CUR_FILE_XML) == 1){
        CMS_LOG(LL_ERROR, "back up current configuration file to backup partition fail!!!");
        ret = CMS_NOK;
    }
#endif

    cms_do_cmd("rm -rf "CMS_CUR_FILE_XML);

exit:
    XML_UNLOCK(&xmlShmCb->cmsMutex->lock);

    return ret;
}

/*
Description:
 cmsXml_SaveDirect -- save cms file and xml file direct, not by msg way.         
                                                                        
 INPUT: xmlShmCb -- share memory control block.         
                                                                 
 OUTPUT: CMS_NOK -- fail.                                            
         CMS_OK  -- success.                                    
                                                                 
 History:                                                         
    06/18/2020 cms:created.                                         
*/
int cmsXml_SaveDirect(cmsXmlCb_t *xmlShmCb)
{
    char cmd[128] = {0};

    if(xmlShmCb == NULL)
        return CMS_NOK;

    /* 
    **save cms file. 
    */
    //if (cmsXml_SaveShmToCmsFile(xmlShmCb,  xmlShmCb->xmlFileName, xmlShmCb->cmsFileName) < 0){
    //    CMS_LOG(LL_ERROR, "ERROR !!! %s(): save %s fail !!!\n", __FUNCTION__, xmlShmCb->cmsFileName);
    //    return CMS_NOK;
    //}

    /* 
    **save xml file, not support when sysdef.xml parse.
    */
    XML_LOCK(&xmlShmCb->cmsMutex->lock);
    if(cmsXml_SaveRamToRom(xmlShmCb, xmlShmCb->xmlFileName) < 0){
        XML_UNLOCK(&xmlShmCb->cmsMutex->lock);
        CMS_LOG(LL_ERROR, "ERROR !!! Save %s fail !!!", xmlShmCb->xmlFileName);
        return CMS_NOK;
    }

    if(cmsXml_openSSL_Enc(CMS_CUR_FILE_XML, CMS_CUR_FILE_ENC) == CMS_OK){
        CMS_LOG(LL_DEBUG, "enc xml success after save.");
        snprintf(cmd, sizeof(cmd) - 1, "md5sum %s > %s", CMS_CUR_FILE_ENC, CMS_CUR_ENC_CRC);
        cms_do_cmd(cmd);
    }else{
        CMS_LOG(LL_ERROR, "enc xml fail after save!!!");
    }
    
    /*save current config to backup partition*/
#if 0
    cmsXml_saveToBak(xmlShmCb);
#endif
    
    snprintf(cmd, sizeof(cmd) - 1, "rm -rf %s", CMS_CUR_FILE_XML);
    //cms_do_cmd(cmd);
    XML_UNLOCK(&xmlShmCb->cmsMutex->lock);
    
    return CMS_OK;
}

cmsXmlCb_t *cms_xmlShm_DefAlloc(char *preDefaultXml){
    cmsXmlCb_t *xmlShmCb = NULL;
    int len = 0;
    char xmlFileName[128] = {0};            /*config.current.xml file path*/
    char cmsFileName[128] = {0};            /*config.current.cms file path*/
    char resCmsFileName[128] = {0};
    char cmdline[512]={0};
    CMS_LOG(LL_FLOOD, "enter\n");

    key_t keys;
    char keyFile[64] = {0};
    int semid = 0;
    struct sembuf sbEnter[2] = { {0, 0, 0}, {0, 1, SEM_UNDO} };
    struct sembuf sbLeave = {0, -1, SEM_UNDO};      /* set to allocate resource */
    int errsv = 0, ret = 0;

    /*create sem*/
    snprintf(keyFile, sizeof(keyFile) - 1, "%s", CMS_DEF_FILE_ENC);
    keys=ftok(keyFile, 'a');
    errsv = errno;
    if (keys == -1){
        if (errsv == ENOENT){
            char *newKeyFile = "/tmp/cmsInitKey";
            ret =CreatShmKeyFile(newKeyFile);
            if(ret == CMS_NOK){
                CMS_LOG(LL_ERROR, "create key file fail by %s", newKeyFile);
                return NULL;
            }
            keys=ftok(newKeyFile, 'a');
        }
        else{
            CMS_LOG(LL_ERROR, "sem ftok errno:%x", errno);
        }
    }
    
    semid = semget( keys, 1, 0666 );

    if (semid == -1){
        CMS_LOG(LL_ERROR, "semget fail %s, errno is:%x", xmlFileName, errno);
        return NULL;
    }
    if (semop(semid, &sbEnter[0], sizeof(sbEnter)/sizeof(struct sembuf)) == -1) {
        CMS_LOG(LL_ERROR, "semop fail, errno %d(%s)", errno, strerror(errno));
        return NULL;
    }

    /*init xml path name*/
    snprintf(xmlFileName, sizeof(xmlFileName) - 1, "%s", CMS_DEF_FILE_ENC);
    snprintf(cmsFileName, sizeof(cmsFileName) - 1, "%s", CMS_PRE_FILE_SHM);
    snprintf(resCmsFileName, sizeof(resCmsFileName) - 1, "/tmp/%s.restore", PRE_CONF_FILE_SHM);
    snprintf(cmdline, sizeof(cmdline) - 1, "ln -s -f %s %s",cmsFileName,resCmsFileName);
    cms_do_cmd(cmdline);
    
    /*alloc memory for xml share memory block*/
    xmlShmCb = malloc(sizeof(cmsXmlCb_t));
    if (xmlShmCb == NULL){
        CMS_LOG(LL_ERROR, "xmlShmCb malloc fail");
        goto error;
    }
    memset(xmlShmCb, 0, sizeof(cmsXmlCb_t));

    /*create cms mutex*/
#ifdef XML_LOCK_SUPPORT
    cmsMutexPack_t *cmsMutex = create_mutex_package(xmlShmCb);
    if(cmsMutex == NULL)
        xmlShmCb->cmsMutex = NULL;
    else
        xmlShmCb->cmsMutex = cmsMutex;
#endif

    /*
    **allocate share memory for tree node and data and 
    **read the tree node and data from file.
    */
    CMS_LOG(LL_PRINT, "resCmsFileName=%s xmlFileName=%s preDefaultXml=%s", resCmsFileName, xmlFileName, preDefaultXml);
    if (cmsXml_InitNodeMemory(resCmsFileName, xmlFileName, preDefaultXml, xmlShmCb, 0) < 0){
        CMS_LOG(LL_ERROR, "xml shm error");
        free(xmlShmCb);
        goto error;
    }

    /*
    **alloctae memory for xmlFile name and cmsFile name
    */
    len = strlen(resCmsFileName);
    xmlShmCb->cmsFileName= calloc(128, 1);    /*1 is for '\0'*/
    if (xmlShmCb->cmsFileName == NULL){
        cms_xmlShmDeInit(&xmlShmCb);
        goto error;
    }
    strncpy(xmlShmCb->cmsFileName, resCmsFileName, len);
    
    len = strlen(xmlFileName);
    xmlShmCb->xmlFileName = calloc(128, 1);
    if (xmlShmCb->xmlFileName == NULL){
        cms_xmlShmDeInit(&xmlShmCb);
        goto error;
    }    
    strncpy(xmlShmCb->xmlFileName, xmlFileName, len);

    /*Remove key file*/
    //sprintf(cmdline,"rm -rf %s%s 2>/dev/null",CMS_KEY_DIR, PRE_CONF_FILE_SHM".restore*");
    //cms_do_cmd(cmdline);
    
    CMS_LOG(LL_FLOOD, "finish xmlShmCb:%"PRIxPTR, (uintptr_t)xmlShmCb);
    if (semop(semid, &sbLeave, sizeof(sbLeave)/sizeof(struct sembuf)) == -1) {
        CMS_LOG(LL_ERROR, "semop fail, errno %d(%s)", errno, strerror(errno));
        return NULL;
    }
    return xmlShmCb;

error:
    if (semop(semid, &sbLeave, sizeof(sbLeave)/sizeof(struct sembuf)) == -1) {
        CMS_LOG(LL_ERROR, "semop fail, errno %d(%s)", errno, strerror(errno));
        return NULL;
    }
    return NULL;
}

/*
Description:
 cms_xmlShmDefaultInit -- init share memmory with default xml value, attach shm to current process.         
                                                                        
 INPUT: None                                                                      
 OUTPUT: NULL -- fail                                            
         cmsXmlCb_t pointer -- success                                        
                                                                 
 History:                                                         
    2/7/2023 cms:created                                         
*/
cmsXmlCb_t *cms_xmlShm_DefInit(void)
{
    cmsXmlCb_t *xmlShmCb = NULL;
    int len = 0;
    char xmlFileName[256] = {0};            /*config.current.xml file path*/
    char cmsFileName[256] = {0};            /*config.current.cms file path*/
    char resCmsFileName[128] = {0};
    char cmdline[512]={0};
    key_t keys;
    char keyFile[64] = {0};
    int semid = 0;
    struct sembuf sbEnter[2] = { {0, 0, 0}, {0, 1, SEM_UNDO} };
    struct sembuf sbLeave = {0, -1, SEM_UNDO};      /* set to allocate resource */
    int errsv = 0, ret = 0;

    /*create sem*/
    snprintf(keyFile, sizeof(keyFile) - 1, "%s", CMS_DEF_FILE_ENC);
    keys=ftok(keyFile, 'a');
    errsv = errno;
    if (keys == -1){
        if (errsv == ENOENT){
            char *newKeyFile = "/tmp/cmsInitKey";
            ret =CreatShmKeyFile(newKeyFile);
            if(ret == CMS_NOK){
                CMS_LOG(LL_ERROR, "create key file fail by %s", newKeyFile);
                return NULL;
            }
            keys=ftok(newKeyFile, 'a');
        }
        else{
            CMS_LOG(LL_ERROR, "sem ftok errno:%x", errno);
        }
    }
    
    semid = semget( keys, 1, 0666 );
    if (semid == -1){
        CMS_LOG(LL_ERROR, "semget fail %s, errno is:%x", xmlFileName, errno);
        return NULL;
    }
    
    if (semop(semid, &sbEnter[0], sizeof(sbEnter)/sizeof(struct sembuf)) == -1) {
        CMS_LOG(LL_ERROR, "semop fail, errno %d(%s)", errno, strerror(errno));
        return NULL;
    }

    /*init xml path name*/
    snprintf(xmlFileName, sizeof(xmlFileName) - 1, "%s", CMS_CUR_FILE_ENC);
    snprintf(cmsFileName, sizeof(cmsFileName) - 1, "%s/%s", CMS_KEY_DIR,  "config.current.cms");
    snprintf(resCmsFileName, sizeof(resCmsFileName) - 1, "%s/%s.restore", CMS_KEY_DIR, PRE_CONF_FILE_SHM);
    if(access(resCmsFileName, F_OK) == -1){
        sprintf(cmdline,"ln -s -f %s %s",cmsFileName,resCmsFileName);
        cms_do_cmd(cmdline);
    }

    xmlShmCb = malloc(sizeof(cmsXmlCb_t));
    if (xmlShmCb == NULL){
        CMS_LOG(LL_ERROR, "xmlShmCb malloc fail");
        goto error;
    }
    memset(xmlShmCb, 0, sizeof(cmsXmlCb_t));
    
    /*allocate share memory for tree node and data and read the tree node and data from file*/
    if (cmsXml_attachXmlShm(xmlShmCb, resCmsFileName) < 0){
        CMS_LOG(LL_ERROR, "xml shm init error.");
        free(xmlShmCb);
        goto error;
    }

    /*alloctae memory for cms file name and xml file name.*/
    len = strlen(cmsFileName);
    xmlShmCb->cmsFileName = calloc((len+1), 1);    /*1 is for '/0*/
    if (xmlShmCb->cmsFileName == NULL){
        cms_xmlShmDeInit(&xmlShmCb);
        goto error;
    }
    strncpy(xmlShmCb->cmsFileName, cmsFileName, len);

    len = strlen(xmlFileName);
    xmlShmCb->xmlFileName = calloc((len+1), 1);
    if (xmlShmCb->xmlFileName == NULL){
        cms_xmlShmDeInit(&xmlShmCb);
        goto error;
    }    
    strncpy(xmlShmCb->xmlFileName, xmlFileName, len);

#ifdef XML_LOCK_SUPPORT
    cmsMutexPack_t *cmsMutex = create_mutex_package(xmlShmCb);
    if(cmsMutex == NULL)
        xmlShmCb->cmsMutex = NULL;
    else
        xmlShmCb->cmsMutex = cmsMutex;
#endif

    CMS_LOG(LL_FLOOD, "finish xmlShmCb:%"PRIxPTR, (uintptr_t)xmlShmCb);
    if (semop(semid, &sbLeave, sizeof(sbLeave)/sizeof(struct sembuf)) == -1) {
        CMS_LOG(LL_ERROR, "semop fail, errno %d(%s)", errno, strerror(errno));
        return NULL;
    }
    return xmlShmCb;

error:
    if (semop(semid, &sbLeave, sizeof(sbLeave)/sizeof(struct sembuf)) == -1) {
        CMS_LOG(LL_ERROR, "semop fail, errno %d(%s)", errno, strerror(errno));
        return NULL;
    }
    return NULL;
}

void cms_log_record(char *logInfo)
{
#ifdef CMS_SUPPORT_YL
    FILE *fp = NULL;
    time_t timeS;
    struct tm *timeP;

    if((logInfo == NULL) || (strlen(logInfo) == 0)){
        CMS_LOG(LL_ERROR, "log info is NULL.");
        return;
    }

    fp = fopen(CMS_LOG_FILE, "a");
    if(fp){
        fprintf(fp, "\n");
        time(&timeS);
        timeP = localtime(&timeS);
        fprintf(fp, "[%d-%d-%d %d:%d:%d][cms_core]",(1900+timeP->tm_year), (1+timeP->tm_mon), timeP->tm_mday, timeP->tm_hour, timeP->tm_min, timeP->tm_sec);
        fprintf(fp, "%s\n", logInfo);
        fclose(fp);
    }else{
        CMS_LOG(LL_ERROR, "fopen cmsLogRecord file %s fail.", CMS_LOG_FILE);
    }
    
    return;
#else
    return;
#endif
}

void cms_log_backUp(void)
{
    struct stat fileBuf;
    char cmd[64] = {0};

    if(stat(CMS_LOG_FILE, &fileBuf) == 0){
        if(fileBuf.st_size >= 16000){
            snprintf(cmd, sizeof(cmd) - 1, "mv %s %s", CMS_LOG_FILE, CMS_LOG_FILE_BAK);
            cms_do_cmd(cmd);
            cms_do_cmd("sync");
        }
    }
    return;
}

int cmsSetLogLevel(cmsMsg_action_t *pAction, char *name)
{
    FILE *fp = NULL;
    char cmdStr[16] = {0};
    uint8_t logLevel = LL_ERROR;
    char *p;

    if(name == NULL){
        CMS_LOG(LL_ERROR, "please set module name!");
        return -1;
    }
    
    if(pAction == NULL){
        if(access(CMS_LOG_LEVEL_FILE, F_OK) == 0){
            fp = fopen(CMS_LOG_LEVEL_FILE, "r");
            if(fp){
                while(fgets(cmdStr, sizeof(cmdStr), fp) != NULL){
                    p = strstr(cmdStr, name);
                    if(p){
                        p += strlen(name);
                        if(*p == '=') p++;
                        logLevel = atoi(p);
                        break;
                    }
                    memset(cmdStr, 0, sizeof(cmdStr));
                }
                fclose(fp);
            }
        }
    }else{
        logLevel = pAction->eventContent->typeSizeOfValue;
    }

    if(logLevel > LL_FLOOD){
        logLevel = LL_ERROR;
    }

    set_log_level(name, logLevel);
    CMS_LOG(LL_PRINT, "set logLevel %d to %s success", logLevel, name);

    return 0;
}
/*****************************************************
 * Add by yulei
 * replace system function to do command
 * return value: 0--OK, !0--fail
 ****************************************************/
int cms_do_cmd(const char *cmd)
{
    pid_t pid, wpid;
    sigset_t tmask, omask;
    int ret = 0, status;

    sigfillset(&tmask);
    sigprocmask(SIG_BLOCK, &tmask, &omask);
    pid = vfork();
    sigprocmask(SIG_SETMASK, &omask, NULL);

    if(pid == 0){
        //fd init
        unsigned long i, fd_max;
        fd_max = sysconf(_SC_OPEN_MAX);
        for(i = 0; i < fd_max; i++){
            if(i != STDOUT_FILENO && i != STDERR_FILENO && i != STDIN_FILENO){
                close(i);
            }
        }

        //signal init
        sigset_t cmask;
        sigemptyset(&cmask);
        sigprocmask(SIG_SETMASK, &cmask, NULL);
        signal(SIGINT, SIG_IGN);

        char *new_argv[4];
        new_argv[0] = "sh";
        new_argv[1] = "-c";
        new_argv[2] = (char *)cmd;
        new_argv[3] = NULL;

        execvp("sh", new_argv);

        //child process do fail
        fprintf(stderr, "[%s:%d]execvp %s failed!!!\n", __FUNCTION__, __LINE__, cmd);
        _exit(EXIT_FAILURE);
    }else if(pid > 0){
        while( (wpid = waitpid(pid, &status, 0)) != pid ){
            if(wpid == -1 && errno == ECHILD){
                fprintf(stderr, "[%s:%d]do_cmd has been interrupt by ECHILD!\n", __FUNCTION__, __LINE__);
                break;
            }else if(wpid == -1 && errno == EINTR){
                fprintf(stderr, "[%s:%d]do_cmd has been interrupt by EINTR!\n", __FUNCTION__, __LINE__);
                continue;
            }
        }

        //ret = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
        ret = status;
    }else if(pid < 0){
        fprintf(stderr, "[%s:%d]vfork for %s failed!\n", __FUNCTION__, __LINE__, cmd);
        ret = -1;
    }

    return ret;
}


